home *** CD-ROM | disk | FTP | other *** search
- ; -*- fundamental -*- (asm-mode sucks)
- ; $Id: memdisk.asm,v 1.26 2004/12/29 19:34:13 hpa Exp $
- ; ****************************************************************************
- ;
- ; memdisk.asm
- ;
- ; A program to emulate an INT 13h disk BIOS from a "disk" in extended
- ; memory.
- ;
- ; Copyright (C) 2001-2004 H. Peter Anvin
- ;
- ; This program is free software; you can redistribute it and/or modify
- ; it under the terms of the GNU General Public License as published by
- ; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
- ; Boston MA 02111-1307, USA; either version 2 of the License, or
- ; (at your option) any later version; incorporated herein by reference.
- ;
- ; ****************************************************************************
-
- %ifndef DEPEND
- %include "../version.gen"
- %endif
-
- ; %define DEBUG_TRACERS ; Uncomment to get debugging tracers
-
- %ifdef DEBUG_TRACERS
-
- %macro TRACER 1
- call debug_tracer
- db %1
- %endmacro
-
- %else ; DEBUG_TRACERS
-
- %macro TRACER 1
- %endmacro
-
- %endif ; DEBUG_TRACERS
-
- org 0h
-
- %define SECTORSIZE_LG2 9 ; log2(sector size)
- %define SECTORSIZE (1 << SECTORSIZE_LG2)
-
- ; Parameter registers definition; this is the definition
- ; of the stack frame.
- %define P_DS word [bp+34]
- %define P_ES word [bp+32]
- %define P_EAX dword [bp+28]
- %define P_HAX word [bp+30]
- %define P_AX word [bp+28]
- %define P_AL byte [bp+28]
- %define P_AH byte [bp+29]
- %define P_ECX dword [bp+24]
- %define P_HCX word [bp+26]
- %define P_CX word [bp+24]
- %define P_CL byte [bp+24]
- %define P_CH byte [bp+25]
- %define P_EDX dword [bp+20]
- %define P_HDX word [bp+22]
- %define P_DX word [bp+20]
- %define P_DL byte [bp+20]
- %define P_DH byte [bp+21]
- %define P_EBX dword [bp+16]
- %define P_HBX word [bp+18]
- %define P_HBXL byte [bp+18]
- %define P_BX word [bp+16]
- %define P_BL byte [bp+16]
- %define P_BH byte [bp+17]
- %define P_EBP dword [bp+8]
- %define P_BP word [bp+8]
- %define P_ESI dword [bp+4]
- %define P_SI word [bp+4]
- %define P_EDI dword [bp]
- %define P_DI word [bp]
-
- section .text
- ; These pointers are used by the installer and
- ; must be first in the binary
- Pointers: dw Int13Start
- dw Int15Start
- dw PatchArea
- dw TotalSize
-
- Int13Start:
- ; Swap stack
- mov [cs:Stack],esp
- mov [cs:SavedAX],ax
- mov ax,ss
- mov [cs:Stack+4],ax
- mov ax,cs
- mov ss,ax
- mov sp,[cs:MyStack]
-
- ; See if DL points to our class of device (FD, HD)
- push dx
- push dx
- xor dl,[cs:DriveNo]
- pop dx
- js .nomatch ; If SF=0, we have a class match here
- jz .our_drive ; If ZF=1, we have an exact match
- cmp dl,[cs:DriveNo]
- jb .nomatch ; Drive < Our drive
- dec dl ; Drive > Our drive, adjust drive #
- .nomatch:
- mov ax,[cs:SavedAX]
- pushf
- call far [cs:OldInt13]
- pushf
- push bp
- mov bp,sp
- cmp byte [cs:SavedAX+1],08h
- je .norestoredl
- cmp byte [cs:SavedAX+1],15h
- jne .restoredl
- test byte [bp+4],80h ; Hard disk?
- jnz .norestoredl
- .restoredl:
- mov dl,[bp+4]
- .norestoredl:
- push ax
- push ebx
- push ds
- mov ax,[bp+2] ; Flags
- lds ebx,[cs:Stack]
- mov [bx+4],al ; Arithmetric flags
- pop ds
- pop ebx
- pop ax
- pop bp
- lss esp,[cs:Stack]
- iret
-
- .our_drive:
- ; Set up standard entry frame
- push ds
- push es
- mov ds,ax
- mov es,ax
- mov ax,[SavedAX]
- pushad
- mov bp,sp ; Point BP to the entry stack frame
- TRACER 'F'
- ; Note: AH == P_AH here
- cmp ah,Int13FuncsMax
- jae Invalid_jump
- xor al,al ; AL = 0 is standard entry condition
- mov di,ax
- shr di,7 ; Convert AH to an offset in DI
- call [Int13Funcs+di]
-
- Done: ; Standard routine for return
- mov P_AX,ax
- DoneWeird:
- TRACER 'D'
- xor bx,bx
- mov es,bx
- mov bx,[StatusPtr]
- mov [es:bx],ah ; Save status
- and ah,ah
-
- lds ebx,[Stack]
- ; This sets the low byte (the arithmetric flags) of the
- ; FLAGS on stack to either 00h (no flags) or 01h (CF)
- ; depending on if AH was zero or not.
- setnz [bx+4] ; Set CF iff error
- popad
- pop es
- pop ds
- lss esp,[cs:Stack]
- iret
-
- Reset:
- ; Reset affects multiple drives, so we need to pass it on
- TRACER 'R'
- test dl,dl ; Always pass it on if we are resetting HD
- js .pass_on ; Bit 7 set
- ; Some BIOSes get very unhappy if we pass a reset floppy
- ; command to them and don't actually have any floppies.
- ; This is a bug, but we have to deal with it nontheless.
- ; Therefore, if we are the *ONLY* floppy drive, and the
- ; user didn't request HD reset, then just drop the command.
- xor ax,ax ; Bottom of memory
- mov es,ax
- ; BIOS equipment byte, top two bits + 1 == total # of floppies
- test byte [es:0x410],0C0h
- jz success
- ; ... otherwise pass it to the BIOS
- .pass_on:
- pop ax ; Drop return address
- popad ; Restore all registers
- pop es
- pop ds
- lss esp,[cs:Stack] ; Restore the stack
- and dl,80h ; Clear all but the type bit
- jmp far [cs:OldInt13]
-
-
- Invalid:
- pop dx ; Drop return address
- Invalid_jump:
- TRACER 'I'
- mov ah,01h ; Unsupported function
- jmp short Done
-
- GetDriveType:
- test byte [DriveNo],80h
- mov bl,02h ; Type 02h = floppy with changeline
- jz .floppy
- ; Hard disks only...
- inc bx ; Type = 03h
- mov dx,[DiskSize] ; Return the disk size in sectors
- mov P_DX,dx
- mov cx,[DiskSize+2]
- mov P_CX,cx
- .floppy:
- mov P_AH,bl ; 02h floppy, 03h hard disk
- pop ax ; Drop return address
- xor ax,ax ; Success...
- jmp short DoneWeird ; But don't stick it into P_AX
-
- GetStatus:
- xor ax,ax
- mov es,ax
- mov bx,[StatusPtr]
- mov ah,[bx] ; Copy last status
- ret
-
- ReadMult:
- TRACER 'm'
- Read:
- TRACER 'R'
- call setup_regs
- do_copy:
- TRACER '<'
- call bcopy
- TRACER '>'
- movzx ax,P_AL ; AH = 0, AL = transfer count
- ret
-
- WriteMult:
- TRACER 'M'
- Write:
- TRACER 'W'
- test byte [ConfigFlags],01h
- jnz .readonly
- call setup_regs
- xchg esi,edi ; Opposite direction of a Read!
- jmp short do_copy
- .readonly: mov ah,03h ; Write protected medium
- ret
-
- ; Verify integrity; just bounds-check
- Seek:
- Verify:
- call setup_regs ; Returns error if appropriate
- ; And fall through to success
-
- CheckIfReady: ; These are always-successful noop functions
- Recalibrate:
- InitWithParms:
- DetectChange:
- SetMode:
- success:
- xor ax,ax ; Always successful
- ret
-
- GetParms:
- TRACER 'G'
- mov dl,[DriveCnt] ; Cached data
- mov P_DL,dl
- test byte [DriveNo],80h
- jnz .hd
- mov P_DI,DPT
- mov P_ES,cs
- mov bl,[DriveType]
- mov P_BL,bl
- .hd:
- mov ax,[Cylinders]
- dec ax ; We report the highest #, not the count
- xchg al,ah
- shl al,6
- or al,[Sectors]
- mov P_CX,ax
- mov ax,[Heads]
- dec ax
- mov P_DH,al
-
- ;
- ; Is this MEMDISK installation check?
- ;
- cmp P_HAX,'ME'
- jne .notic
- cmp P_HCX,'MD'
- jne .notic
- cmp P_HDX,'IS'
- jne .notic
- cmp P_HBX,'K?'
- jne .notic
-
- ; MEMDISK installation check...
- mov P_HAX,'!M'
- mov P_HCX,'EM'
- mov P_HDX,'DI'
- mov P_HBX,'SK'
- mov P_ES,cs
- mov P_DI,MemDisk_Info
-
- .notic:
- xor ax,ax
- ret
-
- ; Set up registers as for a "Read", and compares against disk size
- setup_regs:
-
- ; Convert a CHS address in P_CX/P_DH into an LBA in eax
- ; CH = cyl[7:0]
- ; CL[0:5] = sector (1-based) CL[7:6] = cyl[9:8]
- ; DH = head
- movzx ecx,P_CX
- movzx ebx,cl ; Sector number
- and bl,3Fh
- dec ebx ; Sector number is 1-based
- cmp bx,[Sectors]
- jae .overrun
- movzx edi,P_DH ; Head number
- movzx eax,word [Heads]
- cmp di,ax
- jae .overrun
- shr cl,6
- xchg cl,ch ; Now (E)CX <- cylinder number
- mul ecx ; eax <- Heads*cyl# (edx <- 0)
- add eax,edi
- mul dword [Sectors]
- add eax,ebx
- ; Now eax = LBA, edx = 0
-
- ;
- ; setup_regs continues...
- ;
- ; Note: edi[31:16] and ecx[31:16] = 0 already
- mov di,P_BX ; Get linear address of target buffer
- mov cx,P_ES
- shl ecx,4
- add edi,ecx ; EDI = address to fetch to
- movzx ecx,P_AL ; Sector count
- mov esi,eax
- add eax,ecx ; LBA of final sector + 1
- shl esi,SECTORSIZE_LG2 ; LBA -> byte offset
- add esi,[DiskBuf] ; Get address in high memory
- cmp eax,[DiskSize] ; Check the high mark against limit
- ja .overrun
- shl ecx,SECTORSIZE_LG2-2 ; Convert count to 32-bit words
- ret
-
- .overrun: pop ax ; Drop setup_regs return address
- mov ax,0200h ; Missing address mark
- ret ; Return to Done
-
- int15_e820:
- cmp edx,534D4150h ; "SMAP"
- jne near oldint15
- cmp ecx,20 ; Need 20 bytes
- jb err86
- push ds
- push cs
- pop ds
- and ebx,ebx
- jne .renew
- mov ebx,E820Table
- .renew:
- add bx,12 ; Advance to next
- mov eax,[bx-4] ; Type
- and eax,eax ; Null type?
- jz .renew ; If so advance to next
- mov [es:di+16],eax
- mov eax,[bx-12] ; Start addr (low)
- mov [es:di],eax
- mov ecx,[bx-8] ; Start addr (high)
- mov [es:di+4],ecx
- mov eax,[bx] ; End addr (low)
- mov ecx,[bx+4] ; End addr (high)
- sub eax,[bx-12] ; Derive the length
- sbb ecx,[bx-8]
- mov [es:di+8],eax ; Length (low)
- mov [es:di+12],ecx ; Length (high)
- cmp dword [bx+8],-1 ; Type of next = end?
- jne .notdone
- xor ebx,ebx ; Done with table
- .notdone:
- mov eax,edx ; "SMAP"
- pop ds
- mov ecx,20 ; Bytes loaded
- int15_success:
- mov byte [bp+6], 02h ; Clear CF
- pop bp
- iret
-
- err86:
- mov byte [bp+6], 03h ; Set CF
- mov ah,86h
- pop bp
- iret
-
- Int15Start:
- push bp
- mov bp,sp
- cmp ax,0E820h
- je near int15_e820
- cmp ax,0E801h
- je int15_e801
- cmp ax,0E881h
- je int15_e881
- cmp ah,88h
- je int15_88
- oldint15: pop bp
- jmp far [cs:OldInt15]
-
- int15_e801:
- mov ax,[cs:Mem1MB]
- mov cx,ax
- mov bx,[cs:Mem16MB]
- mov dx,bx
- jmp short int15_success
-
- int15_e881:
- mov eax,[cs:Mem1MB]
- mov ecx,eax
- mov ebx,[cs:Mem16MB]
- mov edx,ebx
- jmp short int15_success
-
- int15_88:
- mov ax,[cs:MemInt1588]
- jmp short int15_success
-
- ;
- ; Routine to copy in/out of high memory
- ; esi = linear source address
- ; edi = linear target address
- ; ecx = 32-bit word count
- ;
- ; Assumes cs = ds = es
- ;
- bcopy:
- push eax
- push ebx
- push edx
- push ebp
-
- test byte [ConfigFlags],02h
- jz .anymode
-
- smsw ax ; Unprivileged!
- test al,01h
- jnz .protmode
-
- .realmode:
- TRACER 'r'
- ; We're in real mode, do it outselves
-
- pushfd
- push ds
- push es
-
- cli
- cld
-
- xor ebx,ebx
- mov bx,cs
- shl ebx,4
- lea edx,[Shaker+ebx]
- mov [Shaker+2],edx
-
- ; Test to see if A20 is enabled or not
- xor ax,ax
- mov ds,ax
- dec ax
- mov es,ax
-
- mov ax,[0]
- mov bx,ax
- xor bx,[es:10h]
- not ax
- mov [0],ax
- mov dx,ax
- xor dx,[es:10h]
- not ax
- mov [0],ax
-
- or dx,bx
- jnz .skip_a20e
-
- mov ax,2401h ; Enable A20
- int 15h
- .skip_a20e:
-
- lgdt [cs:Shaker]
- mov eax,cr0
- or al,01h
- mov cr0,eax
-
- mov bx,8
- mov ds,bx
- mov es,bx
-
- a32 rep movsd
-
- add bx,bx ; BX <- 16
- mov ds,bx
- mov es,bx
-
- and al,~01h
- mov cr0,eax
-
- pop es
- pop ds
-
- and dx,dx
- jnz .skip_a20d
- mov ax,2400h ; Disable A20
- int 15h
- .skip_a20d:
- popfd
- jmp .done
-
- .protmode:
- TRACER 'p'
- .anymode:
-
- .copy_loop:
- push esi
- push edi
- push ecx
- cmp ecx,4000h
- jna .safe_size
- mov ecx,4000h
- .safe_size:
- push ecx ; Transfer size this cycle
- mov eax, esi
- mov [Mover_src1], si
- shr eax, 16
- mov [Mover_src1+2], al
- mov [Mover_src2], ah
- mov eax, edi
- mov [Mover_dst1], di
- shr eax, 16
- mov [Mover_dst1+2], al
- mov [Mover_dst2], ah
- mov si,Mover
- mov ah, 87h
- shl cx,1 ; Convert to 16-bit words
- int 15h
- cli ; Some BIOSes enable interrupts on INT 15h
- pop eax ; Transfer size this cycle
- pop ecx
- pop edi
- pop esi
- jc .error
- lea esi,[esi+4*eax]
- lea edi,[edi+4*eax]
- sub ecx, eax
- jnz .copy_loop
- ; CF = 0
- .error:
- .done:
- pop ebp
- pop edx
- pop ebx
- pop eax
- ret
-
- %ifdef DEBUG_TRACERS
- debug_tracer: pushad
- pushfd
- mov bp,sp
- mov bx,[bp+9*4]
- mov al,[cs:bx]
- inc word [bp+9*4]
- mov ah,0Eh
- mov bx,7
- int 10h
- popfd
- popad
- ret
- %endif
-
- section .data
- alignb 2
- Int13Funcs dw Reset ; 00h - RESET
- dw GetStatus ; 01h - GET STATUS
- dw Read ; 02h - READ
- dw Write ; 03h - WRITE
- dw Verify ; 04h - VERIFY
- dw Invalid ; 05h - FORMAT TRACK
- dw Invalid ; 06h - FORMAT TRACK AND SET BAD FLAGS
- dw Invalid ; 07h - FORMAT DRIVE AT TRACK
- dw GetParms ; 08h - GET PARAMETERS
- dw InitWithParms ; 09h - INITIALIZE CONTROLLER WITH DRIVE PARAMETERS
- dw Invalid ; 0Ah
- dw Invalid ; 0Bh
- dw Seek ; 0Ch - SEEK TO CYLINDER
- dw Reset ; 0Dh - RESET HARD DISKS
- dw Invalid ; 0Eh
- dw Invalid ; 0Fh
- dw CheckIfReady ; 10h - CHECK IF READY
- dw Recalibrate ; 11h - RECALIBRATE
- dw Invalid ; 12h
- dw Invalid ; 13h
- dw Invalid ; 14h
- dw GetDriveType ; 15h - GET DRIVE TYPE
- dw DetectChange ; 16h - DETECT DRIVE CHANGE
- %if 0
- dw Invalid ; 17h
- dw Invalid ; 18h
- dw Invalid ; 19h
- dw Invalid ; 1Ah
- dw Invalid ; 1Bh
- dw Invalid ; 1Ch
- dw Invalid ; 1Dh
- dw Invalid ; 1Eh
- dw Invalid ; 1Fh
- dw Invalid ; 20h
- dw ReadMult ; 21h - READ MULTIPLE
- dw WriteMult ; 22h - WRITE MULTIPLE
- dw SetMode ; 23h - SET CONTROLLER FEATURES
- dw SetMode ; 24h - SET MULTIPLE MODE
- dw Invalid ; 25h - IDENTIFY DRIVE
- dw Invalid ; 26h
- dw Invalid ; 27h
- dw Invalid ; 28h
- dw Invalid ; 29h
- dw Invalid ; 2Ah
- dw Invalid ; 2Bh
- dw Invalid ; 2Ch
- dw Invalid ; 2Dh
- dw Invalid ; 2Eh
- dw Invalid ; 2Fh
- dw Invalid ; 30h
- dw Invalid ; 31h
- dw Invalid ; 32h
- dw Invalid ; 33h
- dw Invalid ; 34h
- dw Invalid ; 35h
- dw Invalid ; 36h
- dw Invalid ; 37h
- dw Invalid ; 38h
- dw Invalid ; 39h
- dw Invalid ; 3Ah
- dw Invalid ; 3Bh
- dw Invalid ; 3Ch
- dw Invalid ; 3Dh
- dw Invalid ; 3Eh
- dw Invalid ; 3Fh
- dw Invalid ; 40h
- dw EDDPresence ; 41h - EDD PRESENCE DETECT
- dw EDDRead ; 42h - EDD READ
- dw EDDWrite ; 43h - EDD WRITE
- dw EDDVerify ; 44h - EDD VERIFY
- dw Invalid ; 45h - EDD LOCK/UNLOCK MEDIA
- dw Invalid ; 46h - EDD EJECT
- dw EDDSeek ; 47h - EDD SEEK
- dw EDDGetParms ; 48h - EDD GET PARAMETERS
- %endif
-
- Int13FuncsEnd equ $
- Int13FuncsMax equ (Int13FuncsEnd-Int13Funcs) >> 1
-
- alignb 8, db 0
- Shaker dw ShakerEnd-$
- dd 0 ; Pointer to self
- dw 0
-
- Shaker_DS: dd 0x0000ffff
- dd 0x008f9300
-
- Shaker_RMDS: dd 0x0000ffff
- dd 0x00009300
-
- ShakerEnd equ $
-
- alignb 8, db 0
-
-
- Mover dd 0, 0, 0, 0 ; Must be zero
- dw 0ffffh ; 64 K segment size
- Mover_src1: db 0, 0, 0 ; Low 24 bits of source addy
- db 93h ; Access rights
- db 00h ; Extended access rights
- Mover_src2: db 0 ; High 8 bits of source addy
- dw 0ffffh ; 64 K segment size
- Mover_dst1: db 0, 0, 0 ; Low 24 bits of target addy
- db 93h ; Access rights
- db 00h ; Extended access rights
- Mover_dst2: db 0 ; High 8 bits of source addy
- Mover_dummy2: dd 0, 0, 0, 0 ; More space for the BIOS
-
- alignb 4, db 0
- MemDisk_Info equ $ ; Pointed to by installation check
- MDI_Bytes dw 27 ; Total bytes in MDI structure
- MDI_Version db VER_MINOR, VER_MAJOR ; MEMDISK version
-
- PatchArea equ $ ; This gets filled in by the installer
-
- DiskBuf dd 0 ; Linear address of high memory disk
- DiskSize dd 0 ; Size of disk in blocks
- CommandLine dw 0, 0 ; Far pointer to saved command line
-
- OldInt13 dd 0 ; INT 13h in chain
- OldInt15 dd 0 ; INT 15h in chain
-
- OldDosMem dw 0 ; Old position of DOS mem end
- BootLoaderID db 0 ; Boot loader ID from header
- ; ---- MDI structure ends here ---
- db 0, 0, 0 ; pad
-
- MemInt1588 dw 0 ; 1MB-65MB memory amount (1K)
-
- Cylinders dw 0 ; Cylinder count
- Heads dw 0 ; Head count
- Sectors dd 0 ; Sector count (zero-extended)
-
- Mem1MB dd 0 ; 1MB-16MB memory amount (1K)
- Mem16MB dd 0 ; 16MB-4G memory amount (64K)
-
- DriveNo db 0 ; Our drive number
- DriveType db 0 ; Our drive type (floppies)
- DriveCnt db 0 ; Drive count (from the BIOS)
-
- ConfigFlags db 0 ; Bit 0 - readonly
-
- MyStack dw 0 ; Offset of stack
- StatusPtr dw 0 ; Where to save status (zeroseg ptr)
-
- DPT times 16 db 0 ; BIOS parameter table pointer (floppies)
-
- ; End patch area
-
- Stack dd 0 ; Saved SS:ESP on invocation
- dw 0
- SavedAX dw 0 ; AX saved on invocation
-
- alignb 4, db 0 ; We *MUST* end on a dword boundary
-
- E820Table equ $ ; The installer loads the E820 table here
- TotalSize equ $ ; End pointer
-